package com.ibeeking.found.uaa.cp.controller.v1;

import com.ibeeking.found.auth.api.common.query.LogoutAuthQuery;
import com.ibeeking.found.auth.api.common.param.RefreshTokenParam;
import com.ibeeking.found.auth.api.feign.CpLoginClient;
import com.ibeeking.found.common.config.secret.RsaConfig;
import com.ibeeking.found.common.constants.HeaderNounConstant;
import com.ibeeking.found.common.enums.LoginModeEnum;
import com.ibeeking.gmc.global.api.common.dto.OauthClientDTO;
import com.ibeeking.gmc.global.api.feign.GlobalOpenFeignClient;
import com.ibeeking.found.uaa.cp.constant.RedisKeyConstant;
import com.ibeeking.found.uaa.cp.model.UserLogoutModel;
import com.ibeeking.nematos.constants.enums.OperateLogTypeEnum;
import com.ibeeking.nematos.log.annotation.Log;
import com.ibeeking.nematos.log.utils.LogUtils;
import com.ibeeking.nematos.redis.utils.RedisUtils;
import com.ibeeking.nematos.utils.encryption.EncryptUtils;
import com.ibeeking.nematos.utils.json.JsonUtils;
import com.ibeeking.nematos.utils.result.ResponseResult;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSONObject;
import org.apache.logging.log4j.util.Strings;
import com.nimbusds.jose.JWSObject;
import java.text.ParseException;
import com.ibeeking.nematos.utils.data.StringUtils;
import javax.annotation.Resource;
import java.security.PublicKey;
import java.util.*;

/**
 * @ClassName UserController控制器
 * @Description 全局用户管理
 * @Author ibeeking
 * @Date 2020-12-02 15:29
 **/
@RestController
@RequestMapping("/uaa/v1")
public class ClientUaaController {

    private static final String GRANT_TYPE = "refresh_token";

    @Resource
    private RsaConfig rsaConfig;
    @Resource
    private AuthorizationServerTokenServices authorizationServerTokenServices;
    @Resource
    private GlobalOpenFeignClient globalOpenFeignClient;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private CpLoginClient cpLoginClient;

    @Log(logType = OperateLogTypeEnum.QUERY, describe = "公钥查询")
    @GetMapping(value = "/oauth/publicKey")
    public ResponseResult<String> publicKey() {
        PublicKey publicKey = rsaConfig.getKeyPair().getPublic();
        String pKey = EncryptUtils.RsaEncrypt.getPublicKey(publicKey);
        return ResponseResult.success(pKey);
    }

    /**
     * 刷新token
     *
     * @param refreshTokenParam
     * @return
     */
    @PostMapping(value = "/oauth/refreshToken")
    public ResponseResult<String> refreshToken(@RequestBody RefreshTokenParam refreshTokenParam) {
        Object granTypeObJ = redisUtils.get(RedisKeyConstant.MODULE + RedisKeyConstant.REFRESH_TOKEN + refreshTokenParam.getJti());
        if (null == granTypeObJ) {
            return ResponseResult.fail("从redis获取refreshToken失败");
        }
        ResponseResult<OauthClientDTO> responseResult = globalOpenFeignClient.queryByClientId(refreshTokenParam.getClientId());
        if (null == responseResult.getData()) {
            return ResponseResult.fail(responseResult.getMsg());
        }
        OauthClientDTO oauthClientDTO = responseResult.getData();
        Map<String, String> requestParameters = new HashMap<>(4);
        requestParameters.put("grant_type", GRANT_TYPE);
        requestParameters.put("client_id", refreshTokenParam.getClientId());
        requestParameters.put("client_secret", EncryptUtils.RsaEncrypt.encrypt(oauthClientDTO.getClientSecret(), rsaConfig.getKeyPair().getPublic()));
        requestParameters.put(GRANT_TYPE, granTypeObJ.toString());
        Set<String> scopes = new HashSet<>();
        scopes.addAll(Arrays.asList(oauthClientDTO.getScope().split(",")));
        TokenRequest tokenRequest = new TokenRequest(requestParameters, refreshTokenParam.getClientId(), scopes, GRANT_TYPE);
        OAuth2AccessToken oAuth2AccessToken = authorizationServerTokenServices.refreshAccessToken(granTypeObJ.toString(), tokenRequest);
        int expiresIn = oAuth2AccessToken.getExpiresIn();
        String refreshToken = oAuth2AccessToken.getRefreshToken().getValue();
        String jti = oAuth2AccessToken.getAdditionalInformation().get(HeaderNounConstant.JWT_JTI).toString().replaceAll("-", "");
        redisUtils.set(RedisKeyConstant.MODULE + RedisKeyConstant.REFRESH_TOKEN + jti, refreshToken, expiresIn - 5);
        return ResponseResult.success(oAuth2AccessToken.getTokenType() + " " + oAuth2AccessToken.getValue());
    }

    @Log(logType = OperateLogTypeEnum.QUERY, describe = "解析token")
    @PostMapping(value = "/resolveToken/{token}")
    public ResponseResult<Map<String, Object>> resolveToken(@PathVariable("token") String token) {
        Map<String, Object> tokenMaps = new HashMap<>();
        try {
            if (StringUtils.isNotBlank(token)) {
                String payload = JWSObject.parse(token.replace(HeaderNounConstant.JWT_PREFIX, Strings.EMPTY)).getPayload().toString();
                JSONObject jsonObject = JSONObject.parseObject(payload);
                tokenMaps =  JSONObject.toJavaObject(jsonObject, Map.class);
            }
        }catch(ParseException ex){
            LogUtils.error("解析Token出错：{}", JsonUtils.toJsonStr(ex));
        }
        return ResponseResult.success(tokenMaps);
    }

    /**
     * 退出登录
     *
     * @param model
     * @return
     */
    @PostMapping(value = "/logout")
    public ResponseResult<Boolean> logout(@RequestBody @Validated UserLogoutModel model) {
        LogoutAuthQuery query = new LogoutAuthQuery();
        query.setLoginMode(LoginModeEnum.WX_CP.getCode());
        query.setRegisterChannel(model.getRegisterChannel().name());
        return cpLoginClient.logout(query);
    }
}
